package compound.repository.entries

import groovy.xml.MarkupBuilder
import types.DBLinkCollection

/**
 * a simple compound
 */
class Compound {

    /**
     * makes this class use the searchable context
     */
    static searchable = {
        //we do not want to add these fields to the index
        inchi index: 'not_analyzed', excludeFromAll: true
        dateCreated index: 'not_analyzed', excludeFromAll: true
        lastUpdated index: 'not_analyzed', excludeFromAll: true

        //association to other classes
        synonyms component: true
        dbLinks component: true
        smiles component: true
        iupac component: true
        inchiHashKey component: true


    }

    SortedSet smiles
    SortedSet dbLinks
    SortedSet iupac
    SortedSet synonyms



    Date dateCreated

    Date lastUpdated

    static hasMany = [
            synonyms: Synonym,
            dbLinks: DBLinks,
            smiles: Smiles,
            iupac: IUPAC
    ]

    static mapping = {
        version false // Required to avoid stale object exceptions when hibernate attempts a lock
        synonyms batchSize: 30
        smiles batchSize: 2
        iupac batchSize: 5
        dbLinks batchSize: 20
    }

    /*
  static fetchMode = {
  synonyms: 'eager'
  smiles: 'eager'
  iupac: 'eager'
  dbLinks: 'eager'
  }
    */

    static constraints = {
        inchi(maxSize: 15000, nullable: false, unique: false)
        formula(maxSize: 5000, nullable: true)
        exactMolareMass(nullable: true)
    }

    static indexes = {
        formulaIndex('formula')
    }

    static belongsTo = InchiHashKey

    InchiHashKey inchiHashKey

    String inchi

    String formula

    double exactMolareMass

    String toString() {
        return "Compound: ${id} - ${inchi}"
    }


    def getDefaultName() {

        if (synonyms == null) {
            return "Unknown"
        }
        if (synonyms.size() > 0) {
            def filtered = getFilteredSynonyms()
            if (filtered.size() > 0) {
                return filtered[0].name
            }
        }
        return "Unknown"

    }

    /**
     * removes duplicatesd synonym, which can be written small and large sometimes. It also removes flagged synonyms. It also sorts the list by rating
     *
     * @return
     */
    def getFilteredSynonyms() {
        List<Synonym> syn = new ArrayList<Synonym>()

        Set<String> index = new HashSet<String>()

        for (Synonym s: synonyms) {
            if (s.flagged == false || s.flagged == null) {

                if (index.contains(s.getName().toLowerCase()) == false) {
                    index.add(s.getName().toLowerCase())
                    syn.add(s)
                }
            }
        }

        log.info "syn: ${syn}"
        syn.sort {Synonym a, Synonym b -> b.rating.compareTo(a.rating)}

        return syn
    }

    /**
     * generates a collection of database links for easier condensed representation
     * @return
     */
    def getDBLinksCollections() {
        Map<String, DBLinkCollection> map = new HashMap<String, DBLinkCollection>()

        Collection<DBLinks> links = DBLinks.findAllByCompound(this)

        links.each {DBLinks link ->

            String name = link.getSourceName()

            if (map.get(name) == null) {
                map.put(name, new DBLinkCollection(name: name))
            }

            map.get(name).links.add(link)

        }

        return map
    }

    def getDBLinksCollectionsXMLData() {
        Map<String, DBLinkCollection> content = getDBLinksCollections()

        def writer = new StringWriter()
        def xml = new MarkupBuilder(writer)

        xml.references(name: "External References") {

            content.keySet().each {

                DBLinkCollection c = content.get(it)

                if (c.getSize() == 1) {
                    DBLinks l = c.getTopElement()

                    reference(name: l.toString(), id: l.id)
                }
                else {
                    reference(name: "${c.name} (${c.getSize()})") {

                        c.links.each {DBLinks l ->
                            link(id: l.id, name: l.toString())
                        }

                    }
                }
            }

        }
        return writer.toString()
    }
}


